#!/usr/bin/env python3
"""
邮件发送工具
支持QQ邮箱、163邮箱、Gmail等主流邮箱服务
使用授权码代替密码进行安全登录
"""

import smtplib
import ssl
import json
import datetime
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
from email.mime.application import MIMEApplication
from email.header import Header
import os
from typing import List, Optional, Dict

class EmailSender:
    """邮件发送器类"""
    
    # 邮箱服务商SMTP服务器配置
    SMTP_SERVERS = {
        'qq': {
            'smtp_server': 'smtp.qq.com',
            'smtp_port': 587,
            'ssl_port': 465
        },
        '163': {
            'smtp_server': 'smtp.163.com',
            'smtp_port': 587,
            'ssl_port': 465
        },
        'gmail': {
            'smtp_server': 'smtp.gmail.com',
            'smtp_port': 587,
            'ssl_port': 465
        },
        'outlook': {
            'smtp_server': 'smtp.office365.com',
            'smtp_port': 587,
            'ssl_port': 465
        }
    }
    
    def __init__(self, email_type: str, sender_email: str, authorization_code: str, use_ssl: bool = True):
        """
        初始化邮件发送器
        
        Args:
            email_type: 邮箱类型 ('qq', '163', 'gmail', 'outlook')
            sender_email: 发件人邮箱地址
            authorization_code: 邮箱授权码
            use_ssl: 是否使用SSL加密连接
        """
        self.email_type = email_type.lower()
        self.sender_email = sender_email
        self.authorization_code = authorization_code
        self.use_ssl = use_ssl
        
        if self.email_type not in self.SMTP_SERVERS:
            raise ValueError(f"不支持的邮箱类型: {email_type}")
        
        self.smtp_config = self.SMTP_SERVERS[self.email_type]
    
    def create_message(self, receiver_email: str, subject: str, content: str, 
                      content_type: str = 'plain', cc_emails: List[str] = None) -> MIMEMultipart:
        """
        创建邮件消息
        
        Args:
            receiver_email: 收件人邮箱
            subject: 邮件主题
            content: 邮件内容
            content_type: 内容类型 ('plain' 或 'html')
            cc_emails: 抄送邮箱列表
            
        Returns:
            MIMEMultipart: 邮件消息对象
        """
        message = MIMEMultipart()
        message['From'] = self.sender_email
        message['To'] = receiver_email
        message['Subject'] = Header(subject, 'utf-8')
        
        if cc_emails:
            message['Cc'] = ', '.join(cc_emails)
        
        # 添加邮件正文
        message.attach(MIMEText(content, content_type, 'utf-8'))
        
        return message
    
    def attach_file(self, message: MIMEMultipart, file_path: str):
        """
        添加附件到邮件
        
        Args:
            message: 邮件消息对象
            file_path: 附件文件路径
        """
        if not os.path.exists(file_path):
            raise FileNotFoundError(f"文件不存在: {file_path}")
        
        filename = os.path.basename(file_path)
        with open(file_path, 'rb') as file:
            attachment = MIMEApplication(file.read(), Name=filename)
        
        attachment['Content-Disposition'] = f'attachment; filename="{filename}"'
        message.attach(attachment)
    
    def send_email(self, receiver_email: str, subject: str, content: str, 
                   content_type: str = 'plain', attachments: List[str] = None,
                   cc_emails: List[str] = None) -> bool:
        """
        发送邮件
        
        Args:
            receiver_email: 收件人邮箱
            subject: 邮件主题
            content: 邮件内容
            content_type: 内容类型
            attachments: 附件文件路径列表
            cc_emails: 抄送邮箱列表
            
        Returns:
            bool: 发送是否成功
        """
        try:
            # 创建邮件消息
            message = self.create_message(receiver_email, subject, content, content_type, cc_emails)
            
            # 添加附件
            if attachments:
                for attachment in attachments:
                    self.attach_file(message, attachment)
            
            # 连接SMTP服务器并发送邮件
            if self.use_ssl:
                # SSL加密连接
                context = ssl.create_default_context()
                with smtplib.SMTP_SSL(self.smtp_config['smtp_server'], self.smtp_config['ssl_port'], context=context) as server:
                    server.login(self.sender_email, self.authorization_code)
                    server.send_message(message)
            else:
                # TLS加密连接
                with smtplib.SMTP(self.smtp_config['smtp_server'], self.smtp_config['smtp_port']) as server:
                    server.starttls()  # 启用TLS加密
                    server.login(self.sender_email, self.authorization_code)
                    server.send_message(message)
            
            print(f"邮件发送成功！收件人: {receiver_email}")
            return True
            
        except Exception as e:
            print(f"邮件发送失败: {str(e)}")
            return False

def get_authorization_code_guide():
    """获取各邮箱授权码申请指南"""
    guide = """
    ===== 各邮箱授权码获取方法 =====
    
    1. QQ邮箱：
       - 登录QQ邮箱 → 设置 → 账户
       - 找到“POP3/IMAP/SMTP/Exchange/CardDAV/CalDAV服务”
       - 开启“POP3/SMTP服务”或“IMAP/SMTP服务”
       - 根据提示发送短信获取授权码
    
    2. 163邮箱：
       - 登录163邮箱 → 设置 → POP3/SMTP/IMAP
       - 开启“SMTP服务”
       - 根据提示获取授权码
    
    3. Gmail：
       - 登录Gmail → 设置 → 查看所有设置 → 转发和POP/IMAP
       - 启用IMAP或POP3
       - 在Google账户设置中启用“两步验证”
       - 生成“应用专用密码”作为授权码
    
    4. Outlook/Hotmail：
       - 登录Outlook → 设置 → 邮件 → 同步电子邮件
       - 开启POP或IMAP
       - 在Microsoft账户安全设置中生成应用密码
    
    注意：授权码不是邮箱登录密码，需要单独获取！
    """
    return guide

"""
邮件发送使用示例
包含多种常见使用场景
"""

# from email_sender import EmailSender
import os

def example_send_text_email():
    """示例1: 发送纯文本邮件"""
    print("示例1: 发送纯文本邮件")
    
    # 从配置文件读取邮箱参数
    config = load_config()
    
    # 增加异常处理：如果配置为空或关键参数缺失，则返回
    if not config or not all(key in config for key in ['email_type', 'sender_email', 'authorization_code']):
        print("警告：配置信息不完整，无法发送邮件")
        return
    
    sender = EmailSender(
        email_type=config.get('email_type', '163'),
        sender_email=config.get('sender_email', 'bcg448251593@163.com'),
        authorization_code=config.get('authorization_code', 'RQREUjVApaMerh6V')
    )
    
    # 发送纯文本邮件
    success = sender.send_email(
        receiver_email='receiver@example.com',
        subject='Python纯文本测试邮件',
        content='这是一封通过Python发送的纯文本测试邮件。\n\n祝好！\nPython邮件工具',
        content_type='plain'
    )
    return success

def example_send_html_email():
    """示例2: 发送HTML格式邮件"""
    print("\n示例2: 发送HTML格式邮件")
    
    # 示例2: 发送HTML格式邮件部分的异常处理
    # 从配置文件读取邮箱参数
    config = load_config()
    
    # 增加异常处理：如果配置为空或关键参数缺失，则返回
    if not config or not all(key in config for key in ['email_type', 'sender_email', 'authorization_code']):
        print("警告：配置信息不完整，无法发送邮件")
        return
    
    sender = EmailSender(
        email_type=config.get('email_type', '163'),
        sender_email=config.get('sender_email', 'bcg448251593@163.com'),
        authorization_code=config.get('authorization_code', 'RQREUjVApaMerh6V')
    )
    
    html_content = """
    <!DOCTYPE html>
    <html>
    <head>
        <meta charset="utf-8">
        <style>
            body { font-family: Arial, sans-serif; margin: 20px; }
            .header { background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; padding: 20px; border-radius: 10px; }
            .content { margin: 20px 0; line-height: 1.6; }
            .footer { margin-top: 30px; padding-top: 20px; border-top: 1px solid #eee; color: #666; }
        </style>
    </head>
    <body>
        <div class="header">
            <h1>🎉 HTML邮件测试</h1>
        </div>
        <div class="content">
            <p>这是一封<strong>HTML格式</strong>的测试邮件！</p>
            <ul>
                <li>支持丰富的样式和布局</li>
                <li>可以嵌入图片和链接</li>
                <li>提供更好的视觉效果</li>
            </ul>
        </div>
        <div class="footer">
            <p>发送自 Python邮件工具</p>
        </div>
    </body>
    </html>
    """
    
    success = sender.send_email(
        receiver_email='receiver@example.com',
        subject='Python HTML格式测试邮件',
        content=html_content,
        content_type='html'
    )
    return success

def load_config(config_file: str = 'user_cfg.json') -> Dict:
    """
    从配置文件加载配置信息
    
    Args:
        config_file: 配置文件路径
        
    Returns:
        Dict: 配置信息字典
    """
    try:
        with open(config_file, 'r', encoding='utf-8') as f:
            config = json.load(f)
        return config
    except Exception as e:
        print(f"加载配置文件失败: {str(e)}")
        return {}

def example_send_email_with_attachment(attach_files: List[str] = None):
    """示例3: 发送带附件的邮件"""
    print("\n示例3: 发送带附件的邮件")
    
    # 从配置文件读取邮箱参数
    config = load_config()
    
    # 增加异常处理：如果配置为空或关键参数缺失，则返回
    if not config or not all(key in config for key in ['email_type', 'sender_email', 'authorization_code', 'receiver_email']):
        print("警告：配置信息不完整，无法发送邮件")
        return
    
    sender = EmailSender(
        email_type=config.get('email_type'),
        sender_email=config.get('sender_email' ),
        authorization_code=config.get('authorization_code')
    )
    print("config param ==> email_type={}, sender_email={}, \n authorization_code={}, receiver_email={}".format(
        config.get('email_type'),
        config.get('sender_email' ),
        config.get('authorization_code'),
        config.get('receiver_email')
    ))
    # 判断attach_files是否为空, 如果不为空,读取第一个附件内容,作为邮件内容, 如果为空,则返回退出
    email_content = None
    if not attach_files or len(attach_files) == 0:
        print("没有附件，邮件发送失败")
        return False
    
    # 读取第一个附件的内容作为邮件内容
    try:
        first_attachment = attach_files[0]
        print(f"读取附件内容: {first_attachment}")
        
        # 尝试不同的编码读取文件
        encodings = ['utf-8', 'gbk', 'gb2312']
        content_read = False
        
        for encoding in encodings:
            try:
                with open(first_attachment, 'r', encoding=encoding) as f:
                    email_content = f.read()
                    email_content = "\r\n" +"="*80+"\r\n" + email_content + "\r\n"
                    email_content = email_content + "="*80+"\r\n"
                    email_content = email_content + "注:机器自动筛选,仅供参考,谨慎判断,请勿盲目操作\r\n"
                    email_content = email_content + "by compute\r\n"
                    email_content = email_content + "="*80+"\r\n"

                print(f"成功使用 {encoding} 编码读取附件内容")
                content_read = True
                break
            except (UnicodeDecodeError, IOError) as e:
                print(f"使用 {encoding} 编码读取附件失败: {str(e)}")
                continue
        
        if not content_read:
            print("无法读取附件内容")
            return False
    except Exception as e:
        print(f"处理附件时出错: {str(e)}")
        return False
    subject_str=f'{datetime.datetime.now().strftime("%Y-%m-%d")}的汇总'
    # 处理多个收件人，将数组转换为逗号分隔的字符串
    receiver_emails = config.get('receiver_email')
    if isinstance(receiver_emails, list):
        receiver_emails_str = ', '.join(receiver_emails)
        print(f"多个收件人: {receiver_emails_str}")
    else:
        receiver_emails_str = receiver_emails
    
    print(f"content_read={email_content}, subject={subject_str}, receiver_email_list={receiver_emails_str}")
    # return False;
    
    success = sender.send_email(
        receiver_email=receiver_emails_str,
        subject=subject_str,
        content=email_content,
        content_type='plain',
        # attachments=[test_file]
        attachments=attach_files#['note.txt', 'note1.txt']
    )
    
    print("send email with attachment success")
    return success


def main():
    """运行所有示例"""
    print("邮件发送示例程序")
    print("=" * 40)
    
    # 显示授权码指南
    # print(get_authorization_code_guide())

    
    # 示例1: 纯文本邮件
    # example_send_text_email()
    
    # 示例2: HTML邮件
    # example_send_html_email()
    
    # 示例3: 带附件邮件
    file_list = ['report_out/bcg_buy_signals_20251104.txt']
    example_send_email_with_attachment(file_list)

if __name__ == "__main__":
    main()